package com.sihai.core;

import java.io.Serializable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.MatchMode;
import org.hibernate.criterion.Restrictions;
import org.hibernate.criterion.Example.PropertySelector;
import org.hibernate.type.Type;

import com.sihai.core.utils.BeanUtils;

/**
 * query的一些工具方法
 */
public final class QueryHelper {

	/**
	 * 设置是否缓存query的结果
	 * 
	 * @param query 要执行的query
	 * @param cacheable 是否缓存
	 * @return 设置后的query
	 */
	public static Query setCacheable(Query query, boolean cacheable) {
		Validate.notNull(query, "query can not be null");
		return query.setCacheable(cacheable);
	}

	/**
	 * 为给定Query设置参数
	 * 
	 * @param query 要设置参数的Query
	 * @param arguments 保存参数值的数组
	 * @return 设置好参数的Query
	 */
	public static Query setParameter(Query query, Object... arguments) {
		Validate.notNull(query, "query can not be null");
		if (arguments == null) {
			return query;
		}
		for (int i = 0; i < arguments.length; i++) {
			query.setParameter(i, arguments[i]);
		}
		return query;
	}

	/**
	 * 为给定Query设置参数
	 * 
	 * @param query 要设置参数的Query
	 * @param argumentsName 保存参数名的数组
	 * @param arguments 保存参数值的数组
	 * @return 设置好参数的Query
	 */
	public static Query setParameter(Query query, String[] argumentsNames, Object[] arguments) {
		Validate.notNull(query, "query can not be null");
		if (argumentsNames == null || arguments == null) {
			return query;
		}

		for (int i = 0; i < argumentsNames.length; i++) {
			String parameterName = argumentsNames[i];
			Object parameterValue = arguments[i];
			if (parameterValue.getClass().isArray()) {
				query.setParameterList(parameterName, (Object[]) parameterValue);
			} else if (parameterValue instanceof Collection<?>) {
				query.setParameterList(parameterName, (Collection<?>) parameterValue);
			} else {
				query.setParameter(parameterName, parameterValue);
			}
		}
		return query;
	}

	/**
	 * 为给定Query设置参数
	 * 
	 * @param query 要设置参数的数组
	 * @param parameterMap 保存参数名于值的Map
	 * @return 设置好参数的Query
	 */
	public static Query setParameter(Query query, Map<String, Object> parameterMap) {
		Validate.notNull(query, "query can not be null");
		if (parameterMap == null) {
			return query;
		}
		for (String parameterName : parameterMap.keySet()) {
			if (parameterName == null) {
				continue;
			}
			Object parameterValue = parameterMap.get(parameterName);
			if (parameterValue == null) {
				continue;
			} else if (parameterValue.getClass().isArray()) {
				query.setParameterList(parameterName, (Object[]) parameterValue);
			} else if (parameterValue instanceof Collection<?>) {
				query.setParameterList(parameterName, (Collection<?>) parameterValue);
			} else {
				query.setParameter(parameterName, parameterValue);
			}
		}
		return query;
	}

	public static Query create(String hql, Session session, Object... arguments) {
		Query query = session.createQuery(hql);
		setParameter(query, arguments);
		return query;
	}

	public static Query create(String hql, Session session, String[] argumentsNames, Object[] arguments) {
		Query query = session.createQuery(hql);
		setParameter(query, argumentsNames, arguments);
		return query;
	}

	// /**
	// * 将动态查询结果构建pagination
	// *
	// * @param criteria 要执行的动态查询对象
	// * @param pageNumber 当前页
	// * @param pageSize 每页条目数
	// * @return 分页对像
	// */
	//
	// @SuppressWarnings({ "rawtypes", "unchecked" })
	// public static <T extends Entity> Pagination<T>
	// constructPagination(Criteria criteria, int pageNumber, int pageSize) {
	// Validate.notNull(criteria, "criteria can not be null");
	//
	// CriteriaImpl impl = (CriteriaImpl) criteria;
	//
	// // 先把Projection和OrderBy条件取出来,清空两者来执行Count操作
	// Projection projection = impl.getProjection();
	// List<CriteriaImpl.OrderEntry> orderEntries;
	// try {
	// orderEntries = BeanUtils.getDeclaredFieldValue(impl, "orderEntries");
	// BeanUtils.setDeclaredFieldValue(impl, "orderEntries",
	// Collections.emptyList());
	// } catch (Exception e) {
	// throw new BaseRuntimeException(e);
	// }
	//
	// // 执行计数查询
	// int count = (Integer)
	// criteria.setProjection(Projections.rowCount()).uniqueResult();
	//
	// if (count < 1) {
	// return Pagination.emptyPagination();
	// }
	//
	// // 将之前的Projection和OrderBy条件重新设回去
	// criteria.setProjection(projection);
	// if (projection == null) {
	// criteria.setResultTransformer(CriteriaSpecification.ROOT_ENTITY);
	// }
	//
	// try {
	// BeanUtils.setDeclaredFieldValue(impl, "orderEntries", orderEntries);
	// } catch (Exception e) {
	// throw new InternalError(" Runtime Exception impossibility throw ");
	// }
	//
	// criteria.setFirstResult((pageNumber - 1) *
	// pageSize).setMaxResults(pageSize);
	// return new Pagination(criteria.list(), count, pageNumber, pageSize);
	// }

	/**
	 * 根据所给对象生成一个动态查询对象，查找所有于所给对象的值匹配的持久化对象
	 * 
	 * @param entity 要匹配的对象
	 * @return 动态查询对象
	 */
	public static DetachedCriteria constructCriteria(final BaseEntity<? extends Serializable> entity) {
		return constructCriteria(entity, null, null);
	}

	/**
	 * 根据所给对象生成一个动态查询对象，查找所有于所给对象的值匹配的持久化对象
	 * 
	 * @param entity 要匹配的对象
	 * @param excludeProperties 不进行查询的属性
	 * @param foreignProperty 查询对象要匹配的一些外键,"foreignKey.keyName"的形式
	 * @param exampleProperty 要对值进行模糊查询的外键
	 * @return 动态查询对象
	 */
	public static DetachedCriteria constructCriteria(final BaseEntity<? extends Serializable> entity,
			final String[] excludeProperties, final String[] foreignProperties) {
		return constructCriteria(entity, excludeProperties, foreignProperties, MatchMode.ANYWHERE);
	}

	public static DetachedCriteria constructCriteria(final BaseEntity<? extends Serializable> entity,
			final String[] excludeProperties, final String[] foreignProperties, MatchMode matchMode) {
		Validate.notNull(entity, "entity can not be null");
		DetachedCriteria criteria = DetachedCriteria.forClass(entity.getClass());

		Example example = Example.create(entity);
		/* 不进行检索的属性 */
		if (excludeProperties != null && excludeProperties.length > 0) {
			for (String name : excludeProperties) {
				example.excludeProperty(name);
			}
		}

		/* 选取pojo中值不为null或空字符串的值 */
		criteria.add(example.setPropertySelector(NOT_EMPTY_OR_ZERO).enableLike(matchMode));

		if (foreignProperties == null) {
			return criteria;
		}

		/* 去除相互包含的关联 a.b.c 覆盖 a.b */
		Set<String> joined = new HashSet<String>();
		for (String foreignProperty : foreignProperties) {
			if (StringUtils.isEmpty(foreignProperty)) {
				continue;
			}

			for (Iterator<String> iterator = joined.iterator(); iterator.hasNext();) {
				String joined_property = iterator.next();
				if (foreignProperty.indexOf(joined_property) == 0) {
					iterator.remove();
				}
			}

			joined.add(foreignProperty);
		}

		for (String joined_property : joined) {
			String[] properties = joined_property.split("\\.");
			DetachedCriteria foreignCriteria = criteria;
			Object keyValue = entity;
			if (keyValue == null) {
				continue;
			}
			for (int i = 0; i < properties.length; i++) {
				String property = properties[i];
				if (keyValue == null) {
					continue;
				}

				/* 得到关联属性的值 */
				keyValue = BeanUtils.getNestedDeclaredFieldValue(keyValue, property);
				if (keyValue == null) {
					continue;
				}

				foreignCriteria = foreignCriteria.createCriteria(property);
				if (BaseEntity.class.isInstance(keyValue)) {
					createExampleCriteria(foreignCriteria, keyValue, matchMode);
				} else {
					foreignCriteria.add(Restrictions.eq(property, keyValue));
				}
			}
		}
		return criteria;
	}

	private static void createExampleCriteria(DetachedCriteria criteria, Object keyValue, MatchMode matchMode) {
		@SuppressWarnings("rawtypes")
		Serializable id = ((BaseEntity) keyValue).getId();
		if (id != null) {
			criteria.add(Restrictions.idEq(id));
		} else {
			Example subExample = Example.create(keyValue).setPropertySelector(NOT_EMPTY_OR_ZERO).enableLike(matchMode);
			criteria.add(subExample);
		}
	}

	private static final PropertySelector NOT_EMPTY_OR_ZERO = new NotEmptyOrZeroPropertySelector();

	/**
	 * 判断pojo的属性值是否为null或空字符串
	 * 
	 * @author pippo
	 */
	private static final class NotEmptyOrZeroPropertySelector implements PropertySelector {

		private static final long serialVersionUID = -1654903406902067747L;

		public boolean include(Object object, String propertyName, Type type) {
			if (object == null) {
				return false;
			}
			if ("".equals(object)) {
				return false;
			}
			if (object instanceof Number && ((Number) object).longValue() == 0) {
				return false;
			}
			return true;
		}
	}
}
